﻿/*
Copyright (c) 2005, Marc Clifton
All rights reserved.
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;

namespace Algobox.Toolbox.Collections
{
    public class CircularList<T> : IEnumerable<T>, IEnumerator<T>
    {
        /// <summary>
        /// Initialises a CircularList with the supplied list size
        /// </summary>
        /// <param name="size">Size of the list</param>
        /// <exception cref="ArgumentOutOfRangeException">Size must be greater than 0</exception>
        public CircularList(int size)
        {
            if (size <= 0)
            {
                throw new ArgumentOutOfRangeException("List size cannot be negative or 0");
            }

            _items = new T[size];
            _index = 0;
            _isLoaded = false;
            _enumeratorIndex = -1;
        }

        protected T[] _items;
        protected int _index;
        protected bool _isLoaded;
        protected int _enumeratorIndex;


        /// <summary>
        /// Returns the position of the circular list indexer.
        /// </summary>
        public int Index
        {
            get { return _index; }
        }

        /// <summary>
        /// Gets/sets the item value at the current index.
        /// </summary>
        public T Value
        {
            get { return _items[_index]; }
            set { _items[_index] = value; }
        }

        /// <summary>
        /// Returns the count of the number of loaded items, up to
        /// and including the total number of items in the collection.
        /// </summary>
        public int Count
        {
            get { return _isLoaded ? _items.Length : _index; }
        }

        /// <summary>
        /// Returns the length of the items array.
        /// </summary>
        public int Length
        {
            get { return _items.Length; }
        }

        /// <summary>
        /// Gets/sets the value at the specified index.
        /// </summary>
        public T this[int index]
        {
            get
            {
                RangeCheck(index);
                return _items[index];
            }
            set
            {
                RangeCheck(index);
                _items[index] = value;
            }
        }

        /// <summary>
        /// Advances to the next item or wraps to the first item.
        /// </summary>
        public void Next()
        {
            if (++_index == _items.Length)
            {
                _index = 0;
                _isLoaded = true;
            }
        }

        /// <summary>
        /// Clears the list, resetting the current index to the 
        /// beginning of the list and flagging the collection as unloaded.
        /// </summary>
        public void Clear()
        {
            _index = 0;
            _items.Initialize();
            _isLoaded = false;
        }

        /// <summary>
        /// Sets all items in the list to the specified value, resets
        /// the current index to the beginning of the list and flags the
        /// collection as loaded.
        /// </summary>
        public void SetAll(T val)
        {
            _index = 0;
            _isLoaded = true;

            for (int i = 0; i < _items.Length; i++)
            {
                _items[i] = val;
            }
        }

        /// <summary>
        /// Internal indexer range check helper.  Throws
        /// ArgumentOutOfRange exception if the index is not valid.
        /// </summary>
        protected void RangeCheck(int index)
        {
            if (index < 0)
            {
                throw new ArgumentOutOfRangeException("Indexer cannot be less than 0.");
            }

            if (index >= _items.Length)
            {
                throw new ArgumentOutOfRangeException("Indexer cannot be greater than or equal to the number of items in the collection.");
            }
        }

        // Interface implementations:

        public IEnumerator<T> GetEnumerator()
        {
            return this;
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return this;
        }

        public T Current
        {
            get { return this[_enumeratorIndex]; }
        }

        public void Dispose()
        {
        }

        object IEnumerator.Current
        {
            get { return this[_enumeratorIndex]; }
        }

        public bool MoveNext()
        {
            ++_enumeratorIndex;
            bool ret = _enumeratorIndex < Count;

            if (!ret)
            {
                Reset();
            }

            return ret;
        }

        public void Reset()
        {
            _enumeratorIndex = -1;
        }
    }
}

